use chrono::{DateTime, NaiveDate};
use chrono_tz::Tz;
use clickhouse_rs::{types::{Decimal, SqlType}, Block};
#[derive(Debug, Clone)]
pub (super) struct ColumnData<'a>{
    name: &'a str,
    datas: Vec<DbValue<'a>>,
}
impl <'a> ColumnData<'a> {
    pub (super) fn new(name: &'a str, datas: Vec<DbValue<'a>>) -> Self {
        Self { name, datas }
    }

    pub(super) fn into_type<T, F>(self, matcher: F, type_name: &str) -> anyhow::Result<Vec<T>>
    where
        F: Fn(DbValue<'a>) -> Option<T>,
    {
        let mut result = Vec::with_capacity(self.datas.len());
        for data in self.datas {
            if let Some(value) = matcher(data) {
                result.push(value);
            } else {
                return Err(anyhow::anyhow!("{}列数据不是{}类型", self.name, type_name));
            }
        }
        Ok(result)
    }

    pub(super) fn into_bool(self) -> anyhow::Result<Vec<bool>> {
        self.into_type(|data| match data {
            DbValue::Bool(v) => Some(v),
            _ => None,
        }, "bool")
    }

    pub(super) fn into_u8(self) -> anyhow::Result<Vec<u8>> {
        self.into_type(|data| match data {
            DbValue::UInt8(v) => Some(v),
            _ => None,
        }, "u8")
    }

    pub(super) fn into_u16(self) -> anyhow::Result<Vec<u16>> {
        self.into_type(|data| match data {
            DbValue::UInt16(v) => Some(v),
            _ => None,
        }, "u16")
    }

    pub(super) fn into_u32(self) -> anyhow::Result<Vec<u32>> {
        self.into_type(|data| match data {
            DbValue::UInt32(v) => Some(v),
            _ => None,
        }, "u32")
    }

    pub(super) fn into_u64(self) -> anyhow::Result<Vec<u64>> {
        self.into_type(|data| match data {
            DbValue::UInt64(v) => Some(v),
            _ => None,
        }, "u64")
    }

    pub(super) fn into_i8(self) -> anyhow::Result<Vec<i8>> {
        self.into_type(|data| match data {
            DbValue::Int8(v) => Some(v),
            _ => None,
        }, "i8")
    }

    pub(super) fn into_i16(self) -> anyhow::Result<Vec<i16>> {
        self.into_type(|data| match data {
            DbValue::Int16(v) => Some(v),
            _ => None,
        }, "i16")
    }

    pub(super) fn into_i32(self) -> anyhow::Result<Vec<i32>> {
        self.into_type(|data| match data {
            DbValue::Int32(v) => Some(v),
            _ => None,
        }, "i32")
    }

    pub(super) fn into_i64(self) -> anyhow::Result<Vec<i64>> {
        self.into_type(|data| match data {
            DbValue::Int64(v) => Some(v),
            _ => None,
        }, "i64")
    }

    pub(super) fn into_str(self) -> anyhow::Result<Vec<&'a str>> {
        self.into_type(|data| match data {
            DbValue::Str(v) => Some(v),
            _ => None,
        }, "&str")
    }

    pub(super) fn into_f32(self) -> anyhow::Result<Vec<f32>> {
        self.into_type(|data| match data {
            DbValue::Float32(v) => Some(v),
            _ => None,
        }, "f32")
    }

    pub(super) fn into_f64(self) -> anyhow::Result<Vec<f64>> {
        self.into_type(|data| match data {
            DbValue::Float64(v) => Some(v),
            _ => None,
        }, "f64")
    }

    pub(super) fn into_date(self) -> anyhow::Result<Vec<NaiveDate>> {
        self.into_type(|data| match data {
            DbValue::Date(v) => Some(v),
            _ => None,
        }, "NaiveDate")
    }

    pub(super) fn into_datetime(self) -> anyhow::Result<Vec<DateTime<Tz>>> {
        self.into_type(|data| match data {
            DbValue::DateTime(v) => Some(v),
            _ => None,
        }, "DateTime")
    }

    pub(super) fn into_decimal(self) -> anyhow::Result<Vec<Decimal>> {
        self.into_type(|data| match data {
            DbValue::Decimal(v) => Some(v),
            _ => None,
        }, "Decimal")
    }
}
#[derive(Debug, Clone)]
pub (super) enum DbValue<'a> {
    Bool(bool),
    UInt8(u8),
    UInt16(u16),
    UInt32(u32),
    UInt64(u64),
    Int8(i8),
    Int16(i16),
    Int32(i32),
    Int64(i64),
    Str(&'a str),
    Float32(f32),
    Float64(f64),
    Date(NaiveDate),
    DateTime(DateTime<Tz>),
    Decimal(Decimal),
}

pub (super) fn add_block_column<'a>(
    block: Block,
    st: &'a SqlType,
    col_data: ColumnData<'a>,
) -> anyhow::Result<Block> {
    let b = match st {
        SqlType::Bool => {
            Ok(block.column(col_data.name, col_data.into_bool()?))
        }
        SqlType::UInt8 => {
            Ok(block.column(col_data.name, col_data.into_u8()?))
        }
        SqlType::UInt16 => {
            Ok(block.column(col_data.name, col_data.into_u16()?))
        }
        SqlType::UInt32 => {
            Ok(block.column(col_data.name, col_data.into_u32()?))
        }
        SqlType::UInt64 => {
            Ok(block.column(col_data.name, col_data.into_u64()?))
        }
        SqlType::Int8 => Ok(block.column(col_data.name, col_data.into_i8()?)),
        SqlType::Int16 => {
            Ok(block.column(col_data.name, col_data.into_i16()?))
        }
        SqlType::Int32 => {
            Ok(block.column(col_data.name, col_data.into_i32()?))
        }
        SqlType::Int64 => {
            Ok(block.column(col_data.name, col_data.into_i64()?))
        }
        SqlType::String => {
            Ok(block.column(col_data.name, col_data.into_str()?))
        }
        SqlType::FixedString(_) => {
            Ok(block.column(col_data.name, col_data.into_str()?))
        }
        SqlType::Float32 => {
            Ok(block.column(col_data.name, col_data.into_f32()?))
        }
        SqlType::Float64 => {
            Ok(block.column(col_data.name, col_data.into_f64()?))
        }
        SqlType::Date => {
            Ok(block.column(col_data.name, col_data.into_date()?))
        }
        SqlType::DateTime(_v) => Ok(block.column(
            col_data.name,
            col_data.into_datetime()?,
        )),
        SqlType::LowCardinality(t) => {
            // 优化 LowCardinality 类型的处理，避免递归调用
            match **t {
                SqlType::String => Ok(block.column(col_data.name, col_data.into_str()?)),
                SqlType::UInt8 => Ok(block.column(col_data.name, col_data.into_u8()?)),
                SqlType::UInt16 => Ok(block.column(col_data.name, col_data.into_u16()?)),
                SqlType::UInt32 => Ok(block.column(col_data.name, col_data.into_u32()?)),
                SqlType::UInt64 => Ok(block.column(col_data.name, col_data.into_u64()?)),
                SqlType::Int8 => Ok(block.column(col_data.name, col_data.into_i8()?)),
                SqlType::Int16 => Ok(block.column(col_data.name, col_data.into_i16()?)),
                SqlType::Int32 => Ok(block.column(col_data.name, col_data.into_i32()?)),
                SqlType::Int64 => Ok(block.column(col_data.name, col_data.into_i64()?)),
                SqlType::Float32 => Ok(block.column(col_data.name, col_data.into_f32()?)),
                SqlType::Float64 => Ok(block.column(col_data.name, col_data.into_f64()?)),
                SqlType::Date => Ok(block.column(col_data.name, col_data.into_date()?)),
                SqlType::DateTime(_) => Ok(block.column(col_data.name, col_data.into_datetime()?)),
                _ => Err(anyhow::anyhow!("LowCardinality类型的子类型{}不支持", t)),
            }
        }
        SqlType::Decimal(_p, _s) => Ok(block.column(
            col_data.name,
            col_data.into_decimal()?,
        )),
        _ => Err(anyhow::anyhow!("{}不支持的类型", st)),
    };
    b
}

